{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第 5 章：神经网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['font.sans-serif']=['SimHei']\n",
    "plt.rcParams['axes.unicode_minus']=False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 逻辑回归"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据集"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从 `sklearn` 库中导入 `make_moon` 数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sklearn.datasets\n",
    "\n",
    "np.random.seed(1)    # 设置随机种子\n",
    "X, Y = sklearn.datasets.make_moons(n_samples=200, noise=.2)    # 导入 make_moon 数据集，样本个数 m=200，噪声标准差为 0.2\n",
    "X, Y = X.T, Y.reshape(1, Y.shape[0])    # X shape: [2,200]，Y shape: [1,200]\n",
    "m = X.shape[1]    # 样本个数\n",
    "dim = X.shape[0]    # 特征维度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X shape:  (2, 200)\n",
      "Y shape:  (1, 200)\n"
     ]
    }
   ],
   "source": [
    "print('X shape: ', X.shape)\n",
    "print('Y shape: ', Y.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "绘制数据分布散点图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.scatter(X[0, Y[0,:]==0], X[1, Y[0,:]==0], c='r', marker='s')    # 负类\n",
    "plt.scatter(X[0, Y[0,:]==1], X[1, Y[0,:]==1], c='b', marker='o')    # 正类\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 梯度下降优化算法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代价函数（Cost Fucntion）："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$J=-\\frac{1}{m}\\sum_{i=1}^{m}y^{(i)}log\\hat y^{(i)}+(1-y^{(i)})log(1-\\hat y^{(i)})$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "梯度下降（Gradient Descent）："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\\frac{\\partial J}{\\partial W} = \\frac{1}{m}X(\\hat Y-Y)^T$$\n",
    "$$\\frac{\\partial J}{\\partial b} = \\frac{1}{m} \\sum_{i=1}^m (\\hat y^{(i)}-y^{(i)})$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义 Sigmoid 函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sigmoid(x):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - x：sigmoid 函数输入\n",
    "\n",
    "    函数输出：\n",
    "        - y：sigmoid 输出\n",
    "    \"\"\"\n",
    "    y = 1 / (1 + np.exp(-x))\n",
    "    \n",
    "    return y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义逻辑回归优化算法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def optimizer(X, Y, num_iterations=200, learning_rate=0.01):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - X：输入数据特征，维度 = (dim, m)\n",
    "        - Y：输入数据标签，维度 = (1, m)\n",
    "        - num_iteration：训练次数\n",
    "        - learning_rate：学习速率\n",
    "    函数输出：\n",
    "        - W：训练后的权重参数\n",
    "        - b：训练后的偏置参数\n",
    "        - cost：每次训练计算的损失存放在 cost 列表中\n",
    "    \"\"\"\n",
    "    cost = []         # 列表，存放每次训练的损失\n",
    "    m = X.shape[1]    # 样本个数\n",
    "    dim = X.shape[0]  # 特征维度\n",
    "    \n",
    "    # 参数初始化\n",
    "    W = np.zeros((dim, 1))\n",
    "    b = 0\n",
    "    \n",
    "    # 迭代训练\n",
    "    for i in range(num_iterations):\n",
    "        Z = np.dot(W.T, X) + b    # 线性部分\n",
    "        Y_hat = sigmoid(Z)    # 非线性部分\n",
    "        J = -1.0 / m * np.sum(Y * np.log(Y_hat) + (1 - Y) * np.log(1 - Y_hat))    # 代价函数\n",
    "        cost.append(J)\n",
    "        \n",
    "        # 梯度下降\n",
    "        dW = 1.0 / m * np.dot(X, (Y_hat - Y).T)\n",
    "        db = 1.0 / m * np.sum(Y_hat - Y)\n",
    "        W = W - learning_rate * dW    # W 更新公式\n",
    "        b = b - learning_rate * db    # b 更新公式\n",
    "    \n",
    "        if (i+1) % 20 == 0:\n",
    "            print('Iteration: %d, J = %f' % (i+1, J))\n",
    "        \n",
    "    return W, b, cost"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration: 20, J = 0.498805\n",
      "Iteration: 40, J = 0.428957\n",
      "Iteration: 60, J = 0.394773\n",
      "Iteration: 80, J = 0.374171\n",
      "Iteration: 100, J = 0.360129\n",
      "Iteration: 120, J = 0.349796\n",
      "Iteration: 140, J = 0.341803\n",
      "Iteration: 160, J = 0.335412\n",
      "Iteration: 180, J = 0.330182\n",
      "Iteration: 200, J = 0.325829\n",
      "Iteration: 220, J = 0.322163\n",
      "Iteration: 240, J = 0.319044\n",
      "Iteration: 260, J = 0.316370\n",
      "Iteration: 280, J = 0.314063\n",
      "Iteration: 300, J = 0.312062\n",
      "Iteration: 320, J = 0.310317\n",
      "Iteration: 340, J = 0.308789\n",
      "Iteration: 360, J = 0.307447\n",
      "Iteration: 380, J = 0.306263\n",
      "Iteration: 400, J = 0.305216\n",
      "Iteration: 420, J = 0.304287\n",
      "Iteration: 440, J = 0.303460\n",
      "Iteration: 460, J = 0.302723\n",
      "Iteration: 480, J = 0.302063\n",
      "Iteration: 500, J = 0.301473\n"
     ]
    }
   ],
   "source": [
    "W, b, cost = optimizer(X, Y, num_iterations=500, learning_rate=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "训练后的参数 $W$ 和 $b$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "W =  [[ 1.14041084]\n",
      " [-3.8333394 ]]\n",
      "b =  0.35047882396508423\n"
     ]
    }
   ],
   "source": [
    "print('W = ', W)\n",
    "print('b = ', b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "显示训练过程中的损失 cost"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(cost)\n",
    "plt.xlabel('迭代次数')\n",
    "plt.ylabel('损失函数')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 预测"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义预测函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def predict(X, W, b):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - X：输入数据特征，维度 = (dim, m)\n",
    "        - W：训练后的权重参数\n",
    "        - b：训练后的偏置参数\n",
    "    函数输出：\n",
    "        - Y_pred：预测输出标签，维度 = (1, m)\n",
    "    \"\"\"\n",
    "    \n",
    "    Y_pred = np.zeros((1, X.shape[1]))    # 初始化 Y_pred\n",
    "    \n",
    "    Z = np.dot(W.T, X) + b    # 线性部分\n",
    "    Y_hat = sigmoid(Z)    # 非线性部分\n",
    "    Y_pred[Y_hat > 0.5] = 1    # Y_hat 大于 0.5 的预测为正类\n",
    "\n",
    "    return Y_pred"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对输入数据 X 进行预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0. 0. 0. 0. 1. 1. 0. 1. 0. 1. 1. 0. 1. 0. 0. 1. 1. 0. 1. 0. 0. 0. 0. 0.\n",
      "  0. 1. 0. 0. 1. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0. 1. 1. 0. 1. 0. 0. 0. 0. 0.\n",
      "  1. 1. 1. 1. 1. 0. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0.\n",
      "  1. 1. 1. 1. 0. 0. 0. 1. 1. 0. 0. 1. 1. 0. 0. 1. 1. 0. 0. 0. 1. 1. 1. 0.\n",
      "  1. 1. 1. 1. 0. 0. 0. 1. 1. 0. 0. 1. 1. 0. 1. 1. 0. 0. 0. 1. 0. 1. 0. 0.\n",
      "  0. 0. 1. 1. 1. 0. 1. 1. 1. 1. 0. 0. 1. 0. 1. 0. 1. 1. 1. 0. 1. 1. 1. 1.\n",
      "  0. 1. 0. 1. 1. 0. 0. 0. 0. 0. 0. 1. 1. 0. 1. 1. 1. 0. 1. 1. 1. 1. 0. 1.\n",
      "  1. 0. 0. 0. 0. 1. 1. 1. 0. 0. 0. 0. 1. 1. 1. 1. 1. 0. 0. 1. 0. 1. 0. 1.\n",
      "  1. 1. 0. 1. 1. 0. 1. 0.]]\n"
     ]
    }
   ],
   "source": [
    "Y_pred = predict(X, W, b)\n",
    "print(Y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算预测准确率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.86\n"
     ]
    }
   ],
   "source": [
    "accuracy = np.mean(Y_pred == Y)\n",
    "print(accuracy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "绘制分类直线，可视化分类效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXsAAAD6CAYAAABApefCAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzt3XuUVNWdL/Dvr6ubpmmexg4iMTPLCT5IgBsfCQYSXhIxGo2EuSZxMrgyClHD0uvEidcxvnHuGEWNWUlsxSuSxHuNcQwExQfiIzgaRYNAvOBoJAbxDc2zu+nu3/2jqujq0+ecOu/a55zvZy0WTfXpOpvqqu/eZ7+OqCqIiCjb6mpdACIiih/DnogoBxj2REQ5wLAnIsoBhj0RUQ4w7ImIcoBhT0SUAwx7IqIcYNgTEeVAfa0LUDa8foAe0jCo1sUgIkqVTe1tH6hqS7XjjAn7QxoG4a5PTa51MYiIUmXShhVbvBzHbhwiohxg2BMR5QDDnogoB4zpsyciCm1IMwaeOxt1h40ERGpdmuioouetd9F+xwPArj2BnoJhT0SZMfDc2WiZMBbDBzRCMhT2qoodB30M758LtC9aGug52I1DRJlRd9jIzAU9AIgIhg9oLF6xBMSwJ6LsEMlc0JeJSKiuKYY9EVFMdu/dA7+3fo3rVrEMeyKimJx76SV48rlnHb9/6b9fj23vvYsHH12JH9+9GLv37sEp3/lH7Gtvj7wsDHsiohj8dOkSvP6XLVi0uBVfPWcuRk88Bs+9/NKB73d1deGMk07GVbcuQqFQQKFQwKI7W3HJvO9iQEND5C18zsYholwa9eBnsbur/zTGwfXN2Pa1lwM/b1dXF264/ad47c0/Y82vH0RDQwOe/+PL+MWDv8HEzx4DAPiobQfOuvB7GNTUhPaODnzrwgtw1N99Cs1NTXjl/72KW+66Ezf/8Goc/slPBi6HFcOeiHLJLujdHvdq0xuvY8++ffj9iy9g9nnn9PnepDmn48tfnIIrL7wYD9/9Czz70ou45a47cel3L8DQIUOwZetf0bZzJ6648GIcNurQUOWwYtgTEUXo00cciYXf/wGeXfsClt+5BB/u2I4333oLx44bj8d+/zReWPdHAMDF112Fv/3EYbj7Rzfj2ZdexMsbNuDGy67AC6+sQ+u9v8S1F18SabkY9kREcShNk1z3pz/hyeefxbHjxgMozrZ59b9ew6Y33sCWrVvx9B+ex3sffIAdO9vw/Lpi91F7RzseefpJnPSlqZEVh2FPRBSD7q5uAMCTzz+LmZO+BFVFoa4ABXD0p8ZgxV33AAA6Ojtxxvx/widGjcKCud/BCcccG0t5OBuHiCgG9/74p/jZL+7B61u24OgxYzD1G1/Hm399C/8y/zwAQHtHB373xOOYc/65uPS8C7D432/C/77//+Jf/u06/Om1zZGXh2FPRLk0uL7Z1+NevbXtbcz89jdwzqXfR2PjANxz0604eMRBWHbnEnyw/SNM/vsz8P6HH2LO+edi85/fwNJFt+FLn5uIpoED0Xr9DZg1ZRquunURNr3xeqhyWElcq7X8OqppuPJOVUQUxqBbLsGnDol2FksQ29vaMGLYMNvv7dqzG0OaBwd63v96523svehHfR6btGHFWlU9rtrPsmVPRBQxp6AHEDjow2LYExHlAMOeiCgHAoe9iIwUkWdcvj9aRP4qIk+W/rQEPRcREYUTaJ69iIwAsASA27D15wEsVNWfBTkHEVEeqWose/IHbdl3AzgTwE6XYyYCOEdEXhKR6wOeh4godbq6ujx9L8ktjgO17FV1J4Bqtc/DAK4FsBfA4yIyXlVfqTxAROYBmAcAIxuaghSFiCiwD9uXYeueRejs2YYBdaMwuvlifGzgaaGf9+KFV2Pzn9+AQNC2aye2t7Xhbz9xGBSKww4ZhTv+1419tjg+dfqJtlscR9nCj3O7hGdVtQMARORlAGMA9Al7VW0F0AoU59nHWBYioj4+bF+GLbsuRw+KrejOnrexZdflABA68H985bUHvn7mD8/jsTVP45r/0buxWS22OI5zNs4jIjJKRAYB+DKADTGei4jIl617Fh0I+rIetGPrnkWRPP8l119r+3h3dzcOGjYcD9/9C/zzufPRPGgQLv3uBfjH2XNw3PgJGDF0KH5yzcJIgx6IKOxFZLqIfM/y8NUAVgN4DsDPVXVTFOciIopCZ882X4/79cwLfzjw9a9X/A4nn/0PmHX2Wbjxjp8DKG5x/OIr63D3j27G5z97DNrbO3DjZVdg3re+jdZ7fxlJGSqF6sZR1amlv58A8ITle6sBHBXm+cksM09fib0N3f0eH7S/gMd+O6sGJSIKbkDdKHT2vG37eBTq6nr72//+lFP7dONwi2Myml3Quz1OZLLRzRf36bMHgDoMxOjmi2M/dy22OGbYE1EulQdho56N09XVhbo65x7yzv2daKhvQEdnJx5f8wxu/9VSXHreBTh+/H/Dhddcgf945GGcPee/Y+yYI0KVw4phT0S59bGBp0Uy1bLS8lWP4We/vAfDhgzFyWf/w4HHy1937O/EDZf+K6665SZM/8JkLF10G4YPHQoAaL3+Bjzx7BpcdesiXHvxJTjy8L+LrFwMeyKiCJ1x0sk446STqx73u8X32D4+/QuTMP0Lk6IuFjdCI6IMUYUp9+iImqoCIf5vbNmTZ4P2Fxxn45iMs4jyo+etd7HjoI9h+IDGWPaXqRVVxY7ODvS89W7g52DYk2dpDUbOIsqP9jsewPvnAh8eNhLIUNhDFT1vvYv2Ox4I/BQMe8oMpxY85ciuPWhftLTWpTAS++wpMxj0RM4Y9kREOcCwJyLKAfbZU2Bpn+Vi+iwioigx7CmwNM1yWXP/KbUuAlFNsRuHMsOppc4WPBFb9hSTSXNW9Pl3El07aeg6IqoVtuwpESZ27RDlCVv2EUp6wDLtA6RElByGfYSSHrD0cr44KwSnvXKIyDwM+4yLswKyqyysffVEZAb22RMR5QDDniLF6Y9EZmI3To7F0Z//2G9n2T7v3oZuzDx9JQeOiWqEYR+hpG/uEfZ8QfvzJ319BWC3VbgCa35zSqpW1npVbftkzoAi0zHsIxT2w+63pe3lfG4VQuDwdbonRIbuFWFV7bVKc0VG+cCwN4hbi7hyloufVqTbcWmcOcO1BUTBBA57ERkJ4H5V/aLD9xsAPADgIACLVfWuoOeivtLciixXMEHDOYtdRERJCBT2IjICwBIAzS6HLQCwVlWvEpGHROTXqroryPkoe0wJZ14pUF4EnXrZDeBMADtdjpkK4L7S108DOC7guXJh5ukrEz9nXNMk0zTNklcKlBeBWvaquhMAxP3u7c0Atpa+/gjASOsBIjIPwDwAGNnQFKQomVGLcAncclU4zsapfN40jgk4qTqgrcmVhSiIOAdodwNoAtAGYHDp332oaiuAVgA4qmk4Py6GYleHhwoswzORKBviXEG7FsDk0tcTALwZ47lyJeluEr9dHTNPXxlbqz7JFbq16FojikskLXsRmQ5grKr+pOLhJQAeEpEvAhgL4PkozmWqOFu/abmlXrWFR5WChnOSVxLst6csCRX2qjq19PcTAJ6wfG+LiMxEsXV/hapm+pOThYE+P2Ftx8/PmtL9w22aKa0uO+X84hcbvF1Fx7qoSlXfRu+MHHKR9FYLdpIKvaT+T16uth777azEBpI59kFhHAj3gLiC1hB5+bAn2SXldUWyGz8VQbX77mbh6o+SFTbgKzHsDZWGVmCa5tNHrbLScqoQGOLkV5ThbsWwN5RprUC7biY/LeTycwSRhoqPKIg4w92KYR8RE/rc4xSmkgkbyklUfOWWeq0Wgk2as4KVVw4kGe5WDPuIZOFDGsV2yGmZJhpWHH3+7PbJnlqGuxXDPiZp7HrI2nbInFZJSTMp3K0Y9jHxOxPE5Eograyvp1sFXPk1KwjyyuRwt2LYG8IaMFkaAzDlKsfLucrHBFlgxooi+9IU7lYMe0P5DcG4A9VLkDlVRGEHWGtR8QUJbQZ9NqU54Csx7DMi7hkrtexiylr3Vpau2rIoK+FuxbA3SND7zJosLztHeu3CycrvNUuyGu5WDPuQHPt2nW7w4VFWugS8/j9M6df3wml6qZcZS3YD9Cb+H7MsL+FuxbAPyTHMpG8ohN1RMoyZp680PkzCdkOlqbKwykrFbqq8hrsVwz4htfxAmxomUfZRe53q6jX8g/SrczaOGRju9hj2GZHGoKlFi9vraxSkbFm8924aOIX7uFGrMeOIJRg28AO0tR+MVZvnYv22aQmXzhwMe59q2R3jJsl92Wut+9it6D5tEzBiH7C9CYVlRwZ+rjR3/+SVl5b7uFGr8dVP34YB9R0AgOFN7+Orn74NAHIb+Ax7n0y4wYepFY6dqKcZdh+7Fd3fXA80lp7zoH3o/uZ6zHgdWPW+/+czbXdR6i9It8yMI5YcCPqyAfUdmHHEEoY9RctPmPndPCxNQeS1dey1Uug+bVNv0Jc1duOcw4OFvSmi2vUyK1cqYfvdhw38wNfjecCwj5BbaNdyIU0aFut4DqIR+2wf/nij93PFfWXkOH5SZTpuFGVK65VK1IOqbe0HY3hT/9q/rf3gSM+TJgz7hCTVqsr8FsPbm4CD+gd+3fYmrLl/+oF/u7Vw4w4+p991XsZUvIh7xsyqzXP79NkDQGdXI1ZtnhvreU3GsK+xqC+7y4FS7eftBjkLa0f7Pl/SCsuO7NtnDwAdhX6DtFnbrjntkp4OWe6X52ycXgx7n6Lujonrstvt550GOQHEGvhRVDDl4+OuqNLQ9WUyE+a6r982LdfhbsWw98mEQa6wXRFOg5zdp22KLeyjrGAKa0fHVs7Md4PFxIRwJ3cM+xSqrHACdUk4DHI6Ph6BWlQwaRPF1URSEwEY7ukTOOxFZDGAsQBWqOp1Nt+vB/BG6Q8ALFDV9UHPRxFyGOTE9qb4zumhgklqHKFWM6PczhvVFWNcV55pDneupC0KFPYiMhtAQVVPEJG7RGSMqr5mOWw8gHtV9QehS0mR8jrIGakqFUyS4wi16oqrPG/lwHzl/j0mzYdPY8Bbg33Te8fjs6NXcSUtgrfspwK4r/T1owAmA7CG/UQAp4rINADrAcxX1a7KA0RkHoB5ADCyIcZWpcHCtjKD/Hxcg5xuM4tWLhvnWsHkrZvHxPnwaQt3L8H+uU8+BLGsbcjrStqgYd8MYGvp648AHGNzzAsATlTVbSJyD4CvAFhWeYCqtgJoBYCjmoZrwLLEzuRViUHPH+UgZ7VFSnsbuqtXMDUYRzBVUltSpy3cK9ntfWMX7NZ/l+VxJW3QsN8NoNwUHwygzuaYV1S1vKLhRQBjAp6r5uJshZnYwvPLa1ldK5iExhFMrrjL4vrdpzncrez2vnEKdjt5XEkbNOzXoth18xyACQA22RyzVEQWAtgA4GsArg94rkxJ0yZmSUpqHCELlatXWQp3qzAt87yupA0a9g8CeEZEDgVwMoBviMh1qnp5xTHXAPgViruBLFPVx8MVNRuyGCpRSGqxVJaVw/33A+egW/YB6Dstt6BNmNx+fw1K5o+X2TNOe9+o2rfwVQFAOBvHL1XdKSJTAcwEcIOqvgNgneWYDSjOyCHyJM7FUqaJYo8ep5Z7Mei9P24Sr/vQO+19oxA01rf3e9629hbc8tTd8RbecIHn2avqdvTOyMkUdrVEi1sP9BfkZjNZ7pYp87oPvdPeNwC4AZoDrqC14TXoGWJFUSwWSuvGbGFUmzabVLj3dvv0VYtuHz/70LvtfcNFVP0x7H3i3in9PfbbWaHCOskFVbW8r4CVXUVYDvjLEnybmdTtE8U+9NwAzR7DPmFe+2rTdNUQNqyTXFBlyvTKsjx0zfhRi33o87KdAsM+YaaFTRQcw/rb69A994/VW/o5WlCVRLgXtMmxW8Z0Se9Dn6cbkzPsKTynUC6UFkVXa+nXYmO2hNSi5Z6G6ZVukuyGydONyRn2Nkzq100Fp7Cu5NItU5ON2WISJNydBkid7lmblvnyaZCnG5Mz7G3UuqslDUv6K8n6FuiX/uJ6M20AjlcAaV5QFUXL3XEg1OH1jHrgNM3dPmHt3T8EzQN22j6eNQx7j5IM4DQs6e8z+6ZHqgc94NotY7egysTpmFkcUM33VYLT/ovG7ssYGMPeozQEcFL6zb4pePhg+OyWqdV9cq2yGO7Ua1DDbl+PpxnDnnyznX1je6AAdRqoVV6r/e0Z7vkSxbz+tGDYk39epkR2FFC4d1zwYE5wOmaaA96k1a9+mTC/vRbz+muFYU/+Oc2+CdGS93yOCKZjmhbuTgOkbseXmbT61Y+o57cHrTiSntdfSwx7A5k+9dNxqqTHlryXgdcop2OaFu5WlS3wp5qq75PQLfs8HWeyKOe3h6048rK9AsPeoyQD2MTplZXCTJX0OvAa5hymh3slxzn2GTdsYP9+crfH3eRpYVQYDHuPTA/gpAXdez6Ogdc0hbuVW9BP2de7BXLaW/JWPVqHgvTYPu5XnhZGhcGwp2R5HHh1uwL44SEL4ywhJaDOJujdHneTpxk1YTDsKTKeFkF5HHh1ugL48MytwFO9D5kwo8NE5UHcqGbrRD3rp629xSGgWzz9fOXvfe/+IejqLqC+0Pt+yeqMmjAY9j6kbRuDJHUfuxXdZ60DGio2PzureKfKPn3xHgZeLzvlfFw54iHbRbmVl+Z52rHQTWV3j1VUs3XCPI9dhVxtyqNbJW79vTcP2Imunnrs6RyCQQ27Wek7YNj7wFW0zrrnbOwN+rIGRfecjZ4GXn94yEKgolvay6V5ngbm0rp/jVOFvHzjAizfuMA20KtV4na/9/q6LuzuHoirn/g/yf4HU4RhT9Fo3u/58cLa0X373Q/p/2NeFrtkYWDOa4ibvkDKiVuFfMtTd9tWytUq8Sz83muBYU+J8DtjxstilywMzKU1xL2qFsx23TXVfiYLv/daYNjHJGj/fmrHBfY0AIP7t+L37B+KqwJOjay22CVrS93TvPVBpcoA71FBQfpvlNfWfrBjd43TtsPlMOetC4Nh2MckaP9+WscFfvP6hTh93C2or+s68JiWPuPjRq2O5YPh1PoHgIumnI1hA99Hj9ahTnpK+5MLBjXsMvbDGsfWB1H19Xt9HmuAF0ShCkjFaHs5mJ26a/Z3DkBnV6NjmPPWhcEEDnsRWQxgLIAVqnpd0GPSxG0VrVOLPKv6dctsK/416+jbMahhF0SKH/DmATtj/WBYW//9w6Y4b7t5wK4Dx6T1wxpEVFcEXp/HLsBFgO6eOtSJ9gnm2eNvsn2OQQ278cAr/+wa5rx1oX+Bwl5EZgMoqOoJInKXiIxR1df8HpM2bt0ok+Y4T3/LAi997uWZElIRrEA0Hwyvl9F2H0w7afywJi1I14VTf3udKK5+5Hd9HnPrew8S5nF1tWRlQDhoy34qgPtKXz8KYDIAa5B7OYYMFXQLgjg+GH4uo/2cJ00f1vJ2CUn13wftuvAzeBpl33ucXS1ZGRAOGvbNALaWvv4IwDFBjhGReQDmAcDIBrPnC2ddVPvLOH0wAMVFU84O1NrycxntfH77sqZNVJumVWsFB+268BPgUfa9x9nVkpWJAEHDfjeAcjoPBmC3e1HVY1S1FUArABzVNDx7N320UW2XTKdxAaDYVRTlrJw4NhBbtXluv4FaoNhva21teb3s9nO1YPfBtGPih9XvvvZBeWkFB71C8xvgUfW9x9nVkpU974OG/VoUu2WeAzABwKaAx+TCmvu971hYDnKnMYAwg8BJ7A65fts0zDr6dtRb+u3Lyq0tAJ4vu/1cRvf9YKZrNo7ffe2BYNM1vbSCw3RdOAW43z51P8c7lXfv/sFVy+tFFva8Dxr2DwJ4RkQOBXAygG+IyHWqernLMRPDFdVsJt5wpFZb/1a7WfOwgR/4uuz2exkd9oOZpjnVQaZremkFR9114bdP3e742eNvxKyjW7Hy1Xm275HTP3Nzn83QAKCxfl9sU3/TJlDYq+pOEZkKYCaAG1T1HQDrqhzTFrKsRqvWtZLEYilT9nWv1m++d/9gX5fdUV5GVwvytM6pntECnHM48PFG4L0O4M43gK6/2B/rpdUeddeF3z51pymcTlN5i1eUragv9F2MVV/XxVlXJYHn2avqdvTOtgl8TF7EsVjKlHC3qtZv3li/r+oqSasoLqO9BPmso1uNmFPtZzHUjBbg+0cCA0sXkYcMLP57xX77Fq3XVnuUXRd++9Td+tqdfh+DGuy7DoPc/SqLuII2hUwN+TJrv3nl6kmg2NrqgLqukoxDtdbluFGrMaihfwUEJDdNs3zlceXAdrS1t3hqTZ9zeG/Qlw0swDYQy8/fUOgoLXTq8XyeMPyOAVS7OrT7fbj9zJUnnZLI/9Nk/u8BRolw6us3fUvbsvXbpuGWp+4GbHelL/brL9+4ADv2tUBVsGNfC5ZvXBDrB7FaK3LGEUv6VUxlfqZpjhu1GhdNORtXnnQqLppyNsaNWu3557766dswvOl9iOiBK49qP//xRvvHrf/fvs8PFOp6sL+7MZEAXLV5Ljq7+hbUrXK3O76S07x9tZnTV17N7fX1zCq27A1S2WI/vut8oMvl4JSIepWkEy+DqtVal06VgSo8X3GE6fMPMle8oE14r2MfDhnY/3vWQPRyZRPXwHSQKZlA3+03ytzm7c8ef6NrOfK8cpphbwDTu2XCSGJBiteArVYW5+l7QzyHQ5jFPUH6rye33481m1Z7eo3dnj9IJeW3cvBbuZeP9zcF0/52h5XStHI6Sgz7hDhNzUxLt0xQSSxI8Rqw1criVBmsfHW+57KEWdwTdG6719fY7fn9VlJ+KoewVwx+Kgkvi+rSuHI6Cgz7mJVb7W7dMlnZx9xJ3AtS/E7jrNZ1ECaYwixGCnMV5OU1dnt+px0orff87bNPfV1Pn2PtKoekp7JaJwcA9tsr5xHDPmJBumTi2Mc8T6LcqCpsxRQ2sIH4roLcnn/GEUtcX0O7fertWCvYWmwPXPk7TNMCubgx7EPKcn97Wpi0UVXYwI77Ksjp+au9hl63jrZWsLXeHjgL2xxEhWHvE8PdPKZtVJXGgKn2GnoJZ7sKNivbA2cBw74Khns6pDFgTeP2GjqFtt0dqCqZdNWVdwx7C4Y7lSXd32ty/7JTaFdbCGfaVVee5T7sw4R7VLNooropNPnnFLBJzyIxfQO2MKHNqy4z5DLso2q9RzWLJgvTK9OkN+D7Ts2rDNikZ5Gk4abWDO10y0XYs2smHiZ3OzixtqCtygGb9CySWs9aKUvj75S8yWTYM9zjZ3q3gxMvUwjLQZfkLBITZq2k9XdK3mQi7BnuyXPqdph19O1GB4OXvc3LLdokZ5GYMGvFxK4kXmlEJ5Vhz3CvPafuhUENu4y+DVyP1qEgPY7fLwds0rNITJi1YkpXUhmvNKKVirA3NdzzPIvGqdtBxP6mGaaocwh6VfS7uUXSA5K1HgA1oSupUpgrDV4R9Gdk2Jsa7lZ5nkVT3DzrRtubfZi8hazTFrjloJ9xxBLMHn9TLgPChK6kSk5dbtW64nhFYM+YsN86vCU1IU+9N3j2cx9ZEzgF2qb3jo88IIK0LmvZIjWhK6mSU5dbj7rfYM/EsQcTGBP2lD4rX51nVEvQC6dAizoggt4MJOmFXHbBbkogOnW5OT1eZtrYgykY9hSYaS1Br+wCrdp+7n5b3EEqjyRbpGno6nDrcnP/ObPGHkzBsKdQTGoJhuEWEEGCMUjrMskWaRq6OoKOIZg29mAKhj0R3AMiSDAGaV3G2SK1Xpk4D356q1iSGFsIeuWY1ivOuPkOexFZDGAsgBWqep3DMfUA3ij9AYAFqro+cCmJYuYWEF5u2WcVpHUZV4vU7spE7W805alicbvSAaIN2aBXjlm54oySr7AXkdkACqp6gojcJSJjVPU1m0PHA7hXVX8QSSmJEuAUEEFa3EFal9b7p/ZoHRoKxSuIyu/7ZXdlIlJcWxDk/qxuq6cb6jqNHgfIM78t+6kA7it9/SiAyQDswn4igFNFZBqA9QDmq2q/222LyDwA8wCgcaj7oAtRrazaPBenf+Zm1Be6DzzW1V2oGoxBWpfl4/2MEVTrUnGbl75jX4vvVrjb6mnrugvTxgHyzDXsReR2AEdWPDQFwOLS1x8BOMbhR18AcKKqbhORewB8BcAy60Gq2gqgFQCGjBrjcGFJZABritmtJguhMrB7VFCo6zu90Ck0vQweu81Xv+Wpu32X1elKx0nepzyawjXsVXV+5b9F5FYA5b0ABgNwWt3wiqqWr/NeBDAmTCGJ/Ih68HDGEUtQX9f3wrS+riuyFqs1sAti3+6pDM3KPfmrtabd5qtfedKpvl8jp7GF/T2NqVtklyd+u3HWoth18xyACQA2ORy3VEQWAtgA4GsArg9cQiIf4pg/HveUSC/bLgO9oVltT35r2ZzmqxcrCfX9GjmNRwDwNcDM/WuS5TfsHwTwjIgcCuBkABNFZCyAb6nq5RXHXQPgVwAEwDJVfTyS0hJV4WeapNewiXuRjpdKozI0vVQOlWWza4lb+e1bdxuP8PKapmFRV9b4CntV3SkiUwHMBHCDqrYBaANwueW4DSjOyCFKlNdWuJ+wiXpKpLWS2bt/MJoH7Op3XHdPHepE+4VmtcrBWjZrSxxQzxvY+W19ex2UTsOirqzxPc9eVbejd0YOkVG8tsL9hs3+nkY0lIah9u4fgpWvzo9sz5yunnp0dRf6zPbp7GrE8o0LfF1pAMUKwu7nKkP4oilne3qN4mx9c/+a5LlvH0eUMqs2z0VnV2Ofx+xa4X6vAJoH7IRIsZ+7oa7Td7nGjVqNi6acjdnjb+xXydTXdaGjuxk79rVAtRjY5fn140attv0/Oi2KqhOtGsReXyO3CjEspy4wDubGh2FPmbJ+2zQs37igFJyCHftabFu6XsMmisArVxjDm/rPnCkb1LALqzbPxf7uRhTqeiDS25K2Bv76bdOwd/9QT+W34/U1irP17bXCoehwbxzKHC/9xl774aMIPK8Dqn66lsJuL+3lNYpzYJr71ySPYU+55DVsogg8rwOqfvbgSSIsoxiYdhvg5f41yWLYU25FeQXgxqnCsN73dsbM1KwvAAAGgUlEQVQRS3xVLHGHZdgKhdMrzcKwJ3IRRQvaqcKw9pObuA97mAqF0yvNwrAnqiJsC9prhZG1fmxOrzQLw54oAV4rjCz1Y/P2gGZh2BPVWFb3iDGxWyrPGPZENZTlQcysdUulHcOeqIayPoiZpW6ptOMKWqIa4iAmJYVhT1RD3COGksKwJ6oh7hFDSWGfPVENcRCTksKwJ6oxDmJSEtiNQ0SUAwx7IqIcYNgTEeUA++yJDJLVrROo9hj2RIbI8tYJVHvsxiEyRJw3+CZi2BMZglsnUJwChb2IjBSRZ6oc0yAiy0VkjYh8J1jxiPKDWydQnHyHvYiMALAEQHOVQxcAWKuqkwDMEZEhAcpHlBvcOoHiFKRl3w3gTAA7qxw3FcB9pa+fBnBcgHMR5cb6bdOwfOMC7NjXAlXBjn0t/e5TSxRU1dk4InI7gCMrHnpCVa8RkWo/2gxga+nrjwCMtHnueQDmAUDj0BYv5SXKNG6dQHGpGvaqOj/gc+8G0ASgDcDg0r+tz90KoBUAhowaowHPQ0REVcQ5G2ctgMmlrycAeDPGcxERkYtIFlWJyHQAY1X1JxUPLwHwkIh8EcBYAM9HcS4iIvIvcMteVadWfP2EJeihqlsAzASwBsCJqtod9FxERBROrNslqOrb6J2RQ0RENcIVtEREOcCwJyLKAYY9EVEOMOyJiHKAYU9ElAMMeyKiHGDYExHlAMOeiCgHGPZERDnAsCciygGGPRFRDjDsiYhygGFPRJQDDHsiohxg2BMR5QDDnogoBxj2REQ5wLAnIsoBhj0RUQ4w7ImIcoBhT0SUAwx7IqIcYNgTEeWAqGqtywAAEJH3AWzxePjBAD6IsThBmVguE8sEsFx+mFgmgOXyI84y/Y2qtlQ7yJiw90NEXlTV42pdDisTy2VimQCWyw8TywSwXH6YUCZ24xAR5QDDnogoB9Ia9q21LoADE8tlYpkAlssPE8sEsFx+1LxMqeyzJyIif9LasiciIh+MD3sRGSkiz1Q5ZrSI/FVEniz9qToNiYi8qfYZ5OevSESGicjDIvKoiPyHiAywOaZeRP5S8VqNS6p8Roe9iIwAsARAc5VDPw9goapOLf15P4GyeamEGkRkuYisEZHvxF2m0jkXi8h/isjlLsck9obzWJ6qxyRdrlp9KD0Ea6LvKY+fwVp8/qoGa+m4JN9bZwFYpKpfBvAOgFk2x4wHcG/Fa7U+gXIBMDzsAXQDOBPAzirHTQRwjoi8JCLXx10oH5XQAgBrVXUSgDkiMiTmcs0GUFDVEwAcLiJjHA5N5A3npTw+ypxouVCDD6XH91Wi7yl4+wwm+vkrqRqsSb+3VPWnqvpY6Z8tAN6zOWwigFNF5A+liqg+zjJVMirsReT2ipbUkwAuUtU2Dz/6MICpAI4HcIKIjI+xmID3SmgqgPtKXz8NIO5FFZXnexTAZIfjknrDeSmPl2Oi5uWctfhQenlfTUWC7ylV3enhM5j0589rsE5F8u8tiMgJAEao6nM2334BwImq+jkADQC+kkSZACCxWsULVZ0f8EefVdUOABCRlwGMAfBKVOUSkdsBHFnx0BOqeo2IVPvRZgBbS19/BGBkVGVyKNcUAIsrzneMw4+W33DbROQeFN9wy6IsW4n1/29XHi/H1KJcSb1GB6jqTgCo8r6K9T0VUKyfPzdVgjXx95aIHATgNgBfdzjklfJrBeBFFF+rRBgV9iE8IiLfBNAG4MsAbo/yyUNUQrsBNKFYrsGlf0fGWi4RubV0PpTO53TlltQbrvz/dyuPl2NqUa6afSiriPU9FVCsnz8nHoI10fdWadzg1wD+p6o67fO1VEQWAtgA4GsAkur2MqsbxwsRmS4i37M8fDWA1QCeA/BzVd2UfMlsrUXvpeMEAG8acr6lIjJBRAoovuHW1bA8Sb9GXs+Z1GvkVy1erwNM+fx5DNakX6t/QvHq4V9LXdFXish1lmOuAbAUwB8B/KeqPh5zmXqpKv8E/APgyYqvpwP4nuX7fwNgI4BbUewWKMRcnqEohtIiAK8CGAZgLIDrLMd9BsXL7PUozqJIqjwTbMrSr8wJ/N68lCuR18jtfWXCe8rUPwDOA7AdwJOlP1ea8N4y+Q9X0MZMRA5FsXXxiHobbA57vhEAZgJ4WlXfift8UZSnFmU27XXyI+n3VJql+fccNYY9EVEOpK7PnoiI/GPYExHlAMOeiCgHGPZERDnAsCciyoH/D4RKIDlbH3WOAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib.colors import ListedColormap\n",
    "    \n",
    "x_min, x_max = X[0, :].min() - 0.5, X[0, :].max() + 0.5\n",
    "y_min, y_max = X[1, :].min() - 0.5, X[1, :].max() + 0.5\n",
    "step = 0.001\n",
    "xx, yy = np.meshgrid(np.arange(x_min, x_max, step), np.arange(y_min, y_max, step))\n",
    "Z = predict(np.c_[xx.ravel(), yy.ravel()].T, W, b)\n",
    "Z = Z.reshape(xx.shape)\n",
    "plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)    # 绘制边界\n",
    "plt.scatter(X[0, Y[0,:]==0], X[1, Y[0,:]==0], c='g', marker='s', label='负类')    # 负类\n",
    "plt.scatter(X[0, Y[0,:]==1], X[1, Y[0,:]==1], c='y', marker='o', label='正类')    # 正类\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 神经网络"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据集"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从 `sklearn` 库中导入 `make_moon` 数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sklearn.datasets\n",
    "\n",
    "np.random.seed(1)    # 设置随机种子\n",
    "X, Y = sklearn.datasets.make_moons(n_samples=200, noise=.2)    # 导入 make_moon 数据集，样本个数 m=200，噪声标准差为 0.2\n",
    "X, Y = X.T, Y.reshape(1, Y.shape[0])    # X shape: [2,200]，Y shape: [1,200]\n",
    "m = X.shape[1]    # 样本个数\n",
    "dim = X.shape[0]    # 特征维度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X shape:  (2, 200)\n",
      "Y shape:  (1, 200)\n"
     ]
    }
   ],
   "source": [
    "print('X shape: ', X.shape)\n",
    "print('Y shape: ', Y.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "绘制数据分布散点图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAX8AAAD6CAYAAABJTke4AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAHZ5JREFUeJzt3W2oZVd5B/D/k8lYcpO5YXIzRCvcMwhBCSYDetUEI1yjhhKkSNpq7aWVWhkZMLUf+sGSgCi5Qj80MFZaM5DS6dxBSAstCBFTiYMSE/WOmkQRacG5Q32hY0YyEyNKJ08/7HMyZ87dL2utvdbe6+X/g8OcOXefs9c5Z59nr/2sZ68tqgoiIirLVWM3gIiIhsfgT0RUIAZ/IqICMfgTERWIwZ+IqEAM/kREBWLwJyIqEIM/EVGBGPyJiAp0tesTReQmAP+mqu9o+PtrAXwTwH9PH/ojVT3X9Ho33nijHjx40LU5RERFOn369C9U9YDt85yCv4jsB3AcwLUti70NwKaq/qPJax48eBDb29suzSEiKpaI7Lg8zzXtcwnABwBcaFnmdgAfEZHviMhnHNdDREQBOAV/Vb2gqi90LPYlAOsA3gLgDhG5bXEBETksItsisn3uXGNGiIiIPAs54PsNVb2oqpcAfBfAzYsLqOoxVV1T1bUDB6xTVkRE5Chk8P+yiLxGRJYA3A3g+wHXRUREFpyrfeaJyF0AblHVz809/CkAXwXwWwCfV9Uf+VgXERH11yv4q+r69N8nADyx8LevAnhDn9cnIqIweJJX6ZaXAZHdt+XlsVtGRAEx+Jfu4kW7x4koCwz+REQFYvAnIioQgz8RUYEY/ImICsTgX7p9++weJ6IseDnJixJ2oW1uPiLKFXv+lCeev0DUisGf8sTzF4haMfgTERWIwZ/SxdQOkTMGf0oXUztEzhj8iYgKxOBPeeL5C0StWOdPeeL5C0St2POn3TiQSpQ9Bn/aLZWBVKZ2iJwx7UPpYmqHyBl7/kREBWLwJzscDyDKAoM/2UllPICIWjH4024cSCXKHoP/2GmMsddf58IFQHX3jQOsRNlg8B87jWGz/hh3FESUJAb/lIy9oyKibDD4kx2OBxBlgSd5kR3m/YmywJ4/EVGBGPzHTmP4XH/fAWGT55c66Fzq+6ZsMe0zdhrDZv379tUP7s52FH0HhE2eX+qgc9v7Fqm+g7G3JSILDP4pYXCJV+47P8oO0z4+NaUGmCZww1QLUTAM/j6Z9P7YQzRXaoqJaADOwV9EbhKRr7f8fa+IfFFEnhSRD7uuhxLCnjlRMpyCv4jsB3AcwLUti90H4LSqvh3AH4oIzwIKbYjKJZPXSqFnbptS4klslBnXnv8lAB8A0DYCuQ7g0en9rwFYW1xARA6LyLaIbJ87d86xKRGYBZKx9Z2QzWTnMb+OlNmmlGbve+zSYCJPnIK/ql5Q1Rc6FrsWwE+m988DuKnmdY6p6pqqrh04cMClKXFIoadrgrN5drtwoT7QX7zIdBclJeSA74sArpnevy7wutLBHqK5WHvZHIimDIQMyKcB3Dm9fwjAmYDrilfMvejYSyl5JEIUjJeTvETkLgC3qOrn5h4+DuAxEXkHgFsAfNPHusgjlx7s8nL+PdzlZe5gKHu9ev6quj7994mFwA9V3QHwHgBPAni3ql7qsy7vYu/1xiqXwN+WOsrlPRK1CJqHV9WfquqjBoPDw/OZt401Nw3428nFUtHkC3v2VDjO7eNDzIHE107OZvkYdnohdU2wR5QAVuCQP2MPyA6VyvMxEM20I42Mwb9kMaerXHRNu7x4a2MbkG2DOctFaWRM+5TMZw891R2GidnOA2iet5/BnBJTbs8/dK+3pMN6n6meoT+3+bSNCQZzykS5wT/0CUSx9AS7dnJd1yCYBd2hUkSxfG5EmWPaJ3ddO7OuoDr7e8wVTbnhZSFpAOX2/Ck/Q4072A4e1+lqK490KDAGf8pHUyrPNGUVaufRtLNIfVpsShqD/7ySBmlLYjq+M7+cy46gbh1N2LOnkTH4z7OtE2/bKeRWQz+UWD63ph0GUSYY/Pto672lMh1xV1A1Cbo+j5hi+Nz6vp/55xNFisE/JmOknZqCrU3Qza08s+/78TEPEo8QKTCWesYktyCaI9+TusV2JEjFYM+/r5IHh3Ob5tmEr7QUe/Y0Mvb85zX16myU1Esv6b36wkFjigR7/vNs68TJDktpy37vFJXygr9tACrhmrWhLO40fY5phNyR9B2EteksLJYRc2dAAykv+NsGoCEDf1vQSC0whC7PND0nY4xy09nzXbCjQQMpL/jHrCtoMDDY42dGVIvB37eSxgdYo06ZO3kSOHgQuOqq6t+TJ8dukT95B/+6vLBPY5+JOrYBzsYN9uPj4DN1OHkSOHwY2NmpNuudner/uewA8g7+POSPi+WRQtAfXwxjPBS1++8HXnrpysdeeql6PAd5B38btimMvqmNEnuelkcKrT++FFJLbdsOU2bRO3vW7vHUlBv8TVMVoVIbbT1PBgYAHT++xe8lxs+sbduZ/a2uHDZUJ6DEDkcPq6t2j6em3OAfsxhmtoyA1Y/P5DOLZbbN+XYMmX5iqsvK5iawtHTlY0tL1eM5YPCnaHn/8cUS5GJpB7Xa2ACOHQMmk2o/PZlU/9/YGLtlfuQ9t4/vGRiHMuuZFn4R79mP7P77q1TP6moV+IP++GLfNmhQGxv5BPtFeff8Q6dPQudQDXuIOdcib2wAZ84AL79c/Rvsh1hoao3KlXfwD61PDtVTDzOmWuScd0JEuWHwH8v8UUkPsdQix7QTykKI9FOMFVE0Ggb/xMVSixzLTqhVLMGvrR0h00+sIqM5zsFfRB4RkadE5IGGv18tImdF5NT0dqt7M6lJLLXIseyEWsUS/GJpBxXNKfiLyL0A9qjqHQBeJyI31yx2G4AvqOr69PZcn4ZmrUePNJZaZNOdEMcFKFW5bbuuPf91AI9O7z8O4M6aZW4H8F4R+db0KCG/slJfaYQePcFYapFNdkIcF2jAM2+j0RTgs9x2VdX6BuARAIem9+8G8ImaZd4C4DXT+/8C4PdrljkMYBvA9urqqlLE9u2r2z1Vj09tbalOJqoi1b9bW1e+xGRS/xKTyYDvI0b1u/7qRkHUbatbW6pLS1d+/EtLl5eNddsFsK0OcVzUodpERI6iSuk8PU0BvUFVP7OwzO+o6m+m9/8SwF5V/bum11xbW9Pt7W3rtlBgJpexNNyGrrqqflGRqo6/WG3TTRR+ol8Is178fIHC0hJwzTXA88/vXn4yqcauYt12ReS0qq7ZPs817XMal1M9hwCcqVnmhIgcEpE9AN4H4BnHddkZ6hC6lEN1j1MRxDI4nRROBeFdU2VaXeAHqhTPDTfU/y3lbdc1+P8HgD8VkYcAvB/AD0TkwYVlPg3gBIDvAXhKVb/i3kwLQ01exUmyrI06OF3Kzpo6uVSgXbwI7N175WOpT/LmFPxV9QKqQd+nAbxTVZ9R1QcWlvm+qt6mqreqakzV3m4WgwdZG3Vwmjtrmmrqra+s7O6czPz2t1UIGLuwwifnOn9V/aWqPqqqP/fZoGjZBAn2JhsNNldPSniGrRempZhNR6BHj1YBvcn583ltuzzDN4SSepMMXP0NNaCbcerLphSz7Qh0Y6P6f52U8/t1nKp9QvBW7dOWkunzXm1TPZF8rr01VfukVoUSarvwZYjPOfbPoIeDB6uAv2gyqXrpNpqqgWJN8wxd7ROHup5ME/ZQ3XAqgmHwc+7F5/QisZw4GVrawb8tvcIfUXRGPT0+lkndUhV5ysh3GXEJY1NpB/8hFRokfAXs0U+PZ8+6n8irpWKZ4yolDP6m6oJH5r1JnwE7iSmfKVkxpGpSm/gt7QHfjAewYuBzEI1TO0Sgz6Ayf2utxhwkLnPAl4JqGizb2bHv4XBqhwgw9RVMike2aQf/zNMuY2sKzCL2qSDmZClnSVzMaEHawZ89maDqAjaw+yjfpIcTQ042eWNW3LCj1SrFI9u0gz8FtbEBfOhDZue3mfRwSiif660twI9ZccOOVqu6jpIIcM8947THBIM/tXrsMbPxvJh7OEmJvKSS6tV1lFSB48fjrfrJM/iPeXgc+ckwXRbL1eqqfRb1zd2nViJHVKeuoxTzoG9+19UFxu09JdxzWyxX29mp9lt1Pf89e6r0zepqFfhdUzh16zx8uLrPtBClJLVB3zx7/uSkrlxNdXfOf2mpOpz1kbtPsUSOqE5qg74M/vSKph6KargqndR6S1FixU0UUitnzjPtQ05WV/2d0dt3nbH2loLbt888Rdg2Ep/LVNwJmXWI7r+/6rz0TYmGxp4/vWKMnktqvaXgFksqXSU89tQmVHGAr9dNqpxZVaO4vfnNb1Zv9u2rq0iuHg9tzHV7sLWlOpmoilT/bm3luc5kuG5P9VX51S1RW1uqS0tXvpWlpf7bS6jXHQqAbXWIuWlP7EZROnkynUPfpLWldrqudZEgnxMNDvG6Q3Gd2I05f/KKpZs92ObpM03tNGk658TkXJQ2pRYdMOdPXrF0s4fCgrmtPXvsHjeVWommLwz+5JVtL4pn9waS4URsly7ZPW6q1KIDBn/yyqYXNfqlHXOW4URsk4nd46ZKnXGWwZ862fTObXpRLikiHil4FGIeqoBzW/nsoS9uR0BCJZq+uJQIhbh5LfVUTb7kMhZ1ZXAiqkeOtD/HpHRTpP4rEjFvS0oleZ1syzP7buMhykE9vGbb9tO1bZlse7ltR3As9Rw96M9u3oN/hnXOY5hMmgN03x9L02tPJn6WT87QHZYIg3+fwGz63Ny2I9fgn2+dPy847UXThdeB/nXQthe95kXgPQvxG+n5mn1q7k2fm9t2xAu4UxBt5W5966BtB9pKLckrSZ+ae9PncjuqMPhTq83N5s6cjx+LzVwopZbklcQkMDcN+psGdW5HUy65ohC3KHL+fXOumQ4yHzmye3B2rAGyouYBCr09hXh9y9dc/D6PHGnP27fl9W3GC8bejnyuHxzwXeCyYfcdAMt4kHm2sQKqe/boKwNkMQffsX/gvWW8Pak2B+sjR5q/t67B2hS+c9/VRq7BP98BXxd9B8AyH2S2HaAd0mwyuZ2d6nT/S5d2X4IylrYay3x7chnczWGw1vdEcoMP+IrIIyLylIg80GeZ0dSdjEKtYp23Z/5MYeDy6f6LQSKGttJlLoO7IQdrhzqBMJaJ5JyCv4jcC2CPqt4B4HUicrPLMqPiZFnWhthoXX6AdTulJrnP1Di0PgHTJZCHGqwdcqqRaKqNXHJFAD4L4J7p/T8G8OeOyxwGsA1ge3V11S3h5aotn8qcf63QJ8e45kKbzhQO2dZBmL6pkQoK+uauXZ8fIq8/5IlfseT8XYP/IwAOTe/fDeATLsvM37wP+HaxCfyFV/vMhD4t3vUH2PS8xVtyp/A3bU+RdC58BMxYBmhtpxrpK4ZqH9ec/4sArpnevw716SOTZeK0uA2YzoQ4m0lxcdrcixe9TW41ptlJWSsrlx8TAT7+8frDftuUgGtaqS4VMN8+INGZGn1dz9eByXfnIw0YyzVvh07FxPC+XQPyaQB3Tu8fAnDGcZk8ZX5Rjl//+vL9X/0KeP753XlSlxyq6w9w/kxh4PLFPSYT4MSJav3FzNTogel3N0TAHGoQtsgTv1wOFwAsA3gGwEMAfogquD/Yscz1ba85eNonZIomssNzn0xSLJOJW0ogt9kWvRtouzL97kJ/X0NvDymey6LqnvZxCv7V+rAfwPsBvLrPMrPb4ME/pIyDv8ngqoh7DnXsHPDY629lul317NjYfHchPy/XMYX5Nq2sVDfT9qXYARk8+Pu+MfinIWTPP6Qs5nk3Depzf9vCB3WCH6vgkk7wY6P3Est359KBqPsObb7PWN67DQb/EFx7UBkH/64f19699vOsjNHmrOd5nwv8S3jR+juI5btz+T5MOydNhq768YHBPwTXIF5AyWfTj+xVr7pyEq4YUiimQaTpq475h19r2vAJfuy8M4vhu3PZCZmmJZuk2AFg8A8h4x68D6n8UEx6c1tbzcuN+X6cgvC04YJLVsEvhoC/yLZNfXv+R47UP+dd74rvs5lh8A+Bwb9VKofIJjupkJerdOWcfnHo+ceS6ukrVM4/linN6zD4h8Dg3yqVnr9JYGtLF/RZb5/eovPnO0072uT8bco7Y+0Bz/Sp9klxqhAG/xAY/FuN2Vu0DUJdy/vekfn4bHwcWZl+TqapsRyODtqYThUS0xEug38ImQ/c+jBGTzBEEPL9mj52JkMeWfVJjaUygGyibjuIcSxoHoM/FSNUUPQZoHz12ofqafdJjZmcuGfzPsbeUSyuv+vSkmNj8B8Ljw4Gl8JAs68d1JCB0FdqbPF1VlbMP4tYU0tj75DaMPiPpS0pSEGkMNAcaxDrw+Q9dVXbdO2sU/huY+Ma/NOZZploKoUZGOdnGhVJdErpBSbvyeaqanWzf8ZyicMS8ALufWV+ke1YzS7YfvZsFUQ2N9MOrLlousD6oqWl+p2h74ubl2DwC7gTjSmGi2HQbk1z+a+smB0FpXBUlwsGfyLypil4Hz1qtrPOMV0Wq6vHbkDy9u2rv0LX4qUciQowC9J9UnIbGwz2Q2DPv6/F66zObqbX/aUoDXX5wNjb4IIpuTSUFfyXl6tjycVb4hdWJ3ttgdXl+sMh2jd2GyhvZVX7sDKnaLMKoZ2dalOY/8rnq09iqDiJoQ2UBlb7ELWY70kDu/f1L71U7RiAOGrNY2hDk1TTUXQlBn/aJccft8nJR7PA2lSu2PR4CDG0oQ7TUflg8Kcr5PrjNukxzwJrDLXmMbShTt1OdP6oidLB4E9XyPXHfcMN7X+fD6wx1JrH0IY6Maej5uV49OpbWQO+y8vNNfkszQTQfnp+JJuKkxtvBJ5/vv5vkwmnhzCVwkD07Oh1vhPTNJ1EDjjga4I1+Z2acsoiafeezp+vf1yEteg2Yk1HzfN19Jr70UNZwZ86bW7WV8Sqpp36iXUANTWxpqPm+UhN5Tr2NY/Bn66wsdGc3oktr2ujq8eaey/Pp9jP4G0a3+ka95mX69jXPAZ/2mUyqX885V5yW481dC/PdcfCHdJ4UhnY7sXlCjAhbsleyStDOV6Fqk3Iq0e5fpaxfAcxX76wiY/LfKZ0RTHwMo7kU4o/elchrwnsGkRiCD6x7IBs+fjsUnrvDP5EjkyChevO0HXHEsNF6mPYAbnwFbhT6QAx+BM56goWfYJJyj3/GHZArlIJ3D64Bn8O+FLxusoX+1R+uNbFD1lP3zSwnHJ5bOwVSVGw3VsAeATAUwAeaFnmagBnAZya3m7tel32/ClWfXvArr3QIXqvbUc1paVPUoUh0j4A7gXwz9P7/wTg5obl3gTgb21em8GfYhVDCiaUrvfWN3CbpNS4Y+jHNfjbXsN3HcCj0/uPA7gTwH/VLHc7gPeKyDsBPAfgo6r6f5brIorC5mb9XDExTWngqqlufTZ/T9/r6XalzOY/19n5FbP1UlitOX8ReVhETs1uAO4D8JPpn88DuKnhqd8G8G5VfSuAvQDuaXj9wyKyLSLb586dc3oDRKHFMqVBiJO+Qs/l1HayVAln0casNfir6kdVdX12A/BZANdM/3xdy/OfVdWfTe9vA7i54fWPqeqaqq4dOHDAvvVEGaoL8i5nIZvsLELP5dQ2aFzEWbQxs8kRAfgzAH89vf8pAH/SsNyjAA4B2APgCVRHAcz5U5KGPOGnaV0rK3bjDjZtrnvd+UHtPrn4tnbkPJYyJAw04LsM4BkADwH4IYDrAdwC4MGF5d4I4FlU+f5Nk9dm8CdXoQcNhwxSTetqC87z2oJqU5tN1tlnZ9f0/aR0Fm3MBgn+1XqwH8D7AbzaZYVNNwZ/cjFEABnyZKemdZkE87rPwqTNJs8LtbOz3XGzOmi3wYJ/qBuDP7kYolceQ89/ZaV7J2fSg29LE82Cqs2OY0g8UqjnGvx5hi8lzWXQ0LZqZsizbZvWdfRod8VR10BpW5vnz4i1ndJ7qKmnWR3kmcseI8SNPX9yYdsr7zPFsu90Q1su3GVdTYPCs8/D5sxi089oyN54ynMNhQSmfahEtsHHZWcRIsccImg2Bf+VFbf2mbzvGFJipVcHMfhTsWwCtE3vMWSv1jSQhXpvvgy5Tub86zH4Exmw6T2G6Gl2lWLOB83QRzU+DL1OVvvsxuBPZMAmoPru1ZqUVM4HzaHGM/pgb3x8rsGf1T5UFJt5enzPZ19XrTJvsRrHtpJpjDmIYpn3iBy47DFC3Njzp9j47tW21dDXpTBKHOBkWsce2PMn8st3r7bpiGEyqb/a1JDnF8TAZfI6csfgT9TC5+UAbYN5aSkVnsQ1LKmOGsa3tram29vbYzeDKKiTJ6tgdvZsdSSwuZlvMLd11VVVj3+RSLXzpXoiclpV12yfZ3slLyLqoe+VsXK2unr5CmKLj5N/TPsQURRKG+MYG4M/UUSGmiQtRqWNcYyNaR+iSMyqXUq+oDnTYsNhz58oEqx2oSEx+BNFghc0pyEx+BNFwvd0EkRtGPyJIsFqFxoSgz9RJFjtQkNitQ9RRFjtQkNhz5+IqEAM/kREBWLwJyIqEIM/EVGBGPyJiArE4E9EVCAGfyKiAjH4E0Ws5CmeKSye5EUUKU7xTCGx508UKU7xTCEx+BNFilM8U0gM/kSR4hTPFJJT8BeRm0Tk6x3L7BWRL4rIkyLyYbfmEZWLUzxTSNbBX0T2AzgO4NqORe8DcFpV3w7gD0Vkn0P7iIrFKZ4pJJee/yUAHwBwoWO5dQCPTu9/DcCaw7qIiraxAZw5A7z8cvUvAz/50lnqKSIPA3j93ENPqOqnRaTrqdcC+Mn0/nkAN9W89mEAhwFglYlMIqLBdAZ/Vf2o42u/COAaAC8AuG76/8XXPgbgGACsra2p43qIiMhSyGqf0wDunN4/BOBMwHUREZEFL2f4ishdAG5R1c/NPXwcwGMi8g4AtwD4po91ERFRf849f1Vdn7v/xELgh6ruAHgPgCcBvFtVL7mui4iI/Ao6t4+q/hSXK36IiCgSPMOXiKhAohpHkY2InAOwM/BqbwTwi4HX2UdK7U2prQDbG1JKbQXSa+/rVdX6JNpopnRW1QNDr1NEtlU1mZPPUmpvSm0F2N6QUmorkGZ7XZ7HtA8RUYEY/ImIClR68D82dgMspdTelNoKsL0hpdRWoJD2RjPgS0REwym9509EVCQGfyLyputCTyLyWhH5HxE5Nb0NXuWXIhG5XkS+JCKPi8i/i8irapa5WkTOzn22t7a9ZjHBP7Wrj4nIIyLylIg80LKM1ZcdgmE7O5cZSldbYvhMF9rTFUxj2mZNLvT0NgCbqro+vZ0bpnVXMgmm0+Vi2XY3ADykqncD+DmA36tZ5jYAX5j7bJ9re8Eign9qVx8TkXsB7FHVOwC8TkRubljU6sv2zaSdFu8lOMO2jPqZzjPcbqPYZqdMLvR0O4CPiMh3ROQzwzSrVmcwjWnbVdV/UNX/nP73AID/rVnsdgDvFZFvTXdaredxFRH8kd7Vx+bb8TguT429yOrLDmAd3e00WWYo6+huy9if6TyT7XYdcWyzUNULqvpCx2JfQtXmtwC4Q0RuC96wGobBdB3xbLsAABG5A8B+VX265s/fRjWJ5lsB7AVwT9trZRn8ReThucP2UwD+ymCjBAyuPhZCTXvvM2yH1ZcdgMnnNcpn2sCkLWN/pq8wDKYxfb4mvqGqF6ez/H4XwGi9aaAzmEb12YrIDQD+HkBTeu9ZVf3Z9P42Oj7baKZ38Cnk1cdCWGyviBydtgPTdjTtpJ9V1d9M73d+2QHMPi+guZ0mywzFpC1jf6a2Rtlme/iyiHwQVXvvBvDwWA2ZC6Z/0LBINNvudEziXwH8zXS6/DonRGQTwPcBvA9Aa1oty55/D7Fcfcy0HSdE5JCI7EH1ZT8zQNvmmbQzls8UMGvL2J+prZg+3yuIyF0i8rGFhz8F4KsAngbweVX90fAtMw6mMX22fwHgTQDun2YIPikiDy4s82kAJwB8D8BTqvqV1ldU1WJuAE7N3b8LwMcW/j4B8AMAR1Ed/u8ZqZ3LqILOQwB+COB6VFdDe3BhuTcCeBbAc6gqKMZu56GaNu56LyN+/ybtHfUzbWj3qem/0W6zqd0AHAHwSwCnprdPxrzthrjxDN8FIvK7qPb2X1azcYJQ7diP6kpoX1PVn4/Vji4m7YzpvcTUFl9i2WZzlOP2MsPgT0RUIOb8iYgKxOBPRFQgBn8iogIx+BMRFYjBn4ioQP8P5uUjS10aXdAAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.scatter(X[0, Y[0,:]==0], X[1, Y[0,:]==0], c='r', marker='s')    # 负类\n",
    "plt.scatter(X[0, Y[0,:]==1], X[1, Y[0,:]==1], c='b', marker='o')    # 正类\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 参数初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def initialize_parameters(n_x, n_h, n_y):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - n_x：输入层维度\n",
    "        - n_h：隐藏层神经元个数\n",
    "        - n_y：输出层神经元个数\n",
    "    函数输出：\n",
    "        - parameters：存储参数字典，W1, b1, W2, b2\n",
    "    \"\"\"\n",
    "    \n",
    "    np.random.seed(0)    # 设置随机种子\n",
    "    \n",
    "    # 参数初始化\n",
    "    W1 = np.random.randn(n_h, n_x)\n",
    "    b1 = np.zeros((n_h, 1))\n",
    "    W2 = np.random.randn(n_y, n_h)\n",
    "    b2 = np.zeros((n_y, 1))\n",
    "    \n",
    "    parameters = {\n",
    "        'W1': W1,\n",
    "        'b1': b1,\n",
    "        'W2': W2,\n",
    "        'b2': b2\n",
    "    }\n",
    "    \n",
    "    return parameters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 前向传播"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义 Sigmoid 激活函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sigmoid(z):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - z：激活函数输入，神经元线性输出\n",
    "        - a：激活函数输出，神经元非线性输出\n",
    "    \"\"\"\n",
    "    \n",
    "    a = 1 / (1 + np.exp(-z))\n",
    "    \n",
    "    return a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义前向传播函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def forward_propagation(X, parameters):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - X：神经网络输入\n",
    "        - parameters：神经网络参数\n",
    "    函数输出：\n",
    "        - A2: 神经网络输出\n",
    "        - cache： 缓存，存储中间变量：Z1, A1, Z2, A2\n",
    "    \"\"\"\n",
    "    \n",
    "    # 神经网络参数\n",
    "    W1 = parameters['W1']\n",
    "    b1 = parameters['b1']\n",
    "    W2 = parameters['W2']\n",
    "    b2 = parameters['b2']\n",
    "    \n",
    "    # 输入层 —> 隐藏层\n",
    "    Z1 = np.dot(W1, X) + b1\n",
    "    A1 = np.tanh(Z1)\n",
    "    # 隐藏层 —> 输出层\n",
    "    Z2 = np.dot(W2, A1) + b2\n",
    "    A2 = sigmoid(Z2)\n",
    "    \n",
    "    cache = {\n",
    "        'Z1': Z1,\n",
    "        'A1': A1,\n",
    "        'Z2': Z2,\n",
    "        'A2': A2\n",
    "    }\n",
    "    \n",
    "    return A2, cache"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 计算损失"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_loss(A2, Y):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - A2：神经网络输出\n",
    "        - Y：样本真实标签\n",
    "    函数输出：\n",
    "        - cost: 神经网络交叉熵损失\n",
    "    \"\"\"\n",
    "    \n",
    "    #样本个数\n",
    "    m = Y.shape[1]\n",
    "    \n",
    "    cross_entropy = -(Y * np.log(A2) + (1 - Y) * np.log(1 - A2))\n",
    "    cost = 1.0 / m * np.sum(cross_entropy)\n",
    "    \n",
    "    return cost"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 反向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def back_propagation(X, Y, parameters, cache):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - X：神经网络输入\n",
    "        - Y：样本真实标签\n",
    "        - parameters: 网络参数\n",
    "        - cache: 缓存，存储中间变量：Z1, A1, Z2, A2\n",
    "    函数输出：\n",
    "        - grads: 神经网络参数梯度\n",
    "    \"\"\"\n",
    "    \n",
    "    # 样本个数\n",
    "    m = X.shape[1]\n",
    "    \n",
    "    # 神经网络参数\n",
    "    W1 = parameters['W1']\n",
    "    b1 = parameters['b1']\n",
    "    W2 = parameters['W2']\n",
    "    b2 = parameters['b2']\n",
    "    \n",
    "    # 中间变量\n",
    "    Z1 = cache['Z1']\n",
    "    A1 = cache['A1']\n",
    "    Z2 = cache['Z2']\n",
    "    A2 = cache['A2']\n",
    "    \n",
    "    # 计算梯度\n",
    "    dZ2 = A2 - Y\n",
    "    dW2 = 1.0 / m * np.dot(dZ2, A1.T)\n",
    "    db2 = 1.0 / m * np.sum(dZ2, axis=1, keepdims=True)\n",
    "    dZ1 = np.dot(W2.T, dZ2) * (1 - np.power(A1, 2))\n",
    "    dW1 = 1.0 / m * np.dot(dZ1, X.T)\n",
    "    db1 = 1.0 / m * np.sum(dZ1, axis=1, keepdims=True)\n",
    "    \n",
    "    grads = {\n",
    "        'dW1': dW1,\n",
    "        'db1': db1,\n",
    "        'dW2': dW2,\n",
    "        'db2': db2\n",
    "    }\n",
    "    \n",
    "    return grads"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 更新参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def update_parameters(parameters, grads, learning_rate=0.1):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - parameters: 网络参数\n",
    "        - grads: 神经网络参数梯度\n",
    "    函数输出：\n",
    "        - parameters: 网络参数\n",
    "    \"\"\"\n",
    "    \n",
    "    # 神经网络参数\n",
    "    W1 = parameters['W1']\n",
    "    b1 = parameters['b1']\n",
    "    W2 = parameters['W2']\n",
    "    b2 = parameters['b2']\n",
    "    \n",
    "    # 神经网络参数梯度\n",
    "    dW1 = grads['dW1']\n",
    "    db1 = grads['db1']\n",
    "    dW2 = grads['dW2']\n",
    "    db2 = grads['db2']\n",
    "    \n",
    "    # 梯度下降算法\n",
    "    W1 = W1 - learning_rate * dW1\n",
    "    b1 = b1 - learning_rate * db1\n",
    "    W2 = W2 - learning_rate * dW2\n",
    "    b2 = b2 - learning_rate * db2\n",
    "    \n",
    "    parameters = {\n",
    "        'W1': W1,\n",
    "        'b1': b1,\n",
    "        'W2': W2,\n",
    "        'b2': b2\n",
    "    }\n",
    "    \n",
    "    return parameters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 构建神经网络模型并训练"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义模型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def nn_model(X, Y, n_h=3, num_iterations=200, learning_rate=0.1):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - X：神经网络输入\n",
    "        - Y：样本真实标签\n",
    "        - n_h: 隐藏层神经元个数\n",
    "        - num_iterations: 训练次数\n",
    "        - learning_rate: 学习率\n",
    "    函数输出：\n",
    "        - parameters: 训练完成后的网络参数\n",
    "    \"\"\"\n",
    "    \n",
    "    # 定义网络\n",
    "    n_x = X.shape[0]\n",
    "    n_y = 1\n",
    "    \n",
    "    # 参数初始化\n",
    "    parameters = initialize_parameters(n_x, n_h, n_y)\n",
    "    \n",
    "    # 迭代训练\n",
    "    for i in range(num_iterations):\n",
    "        # 正向传播\n",
    "        A2, cache = forward_propagation(X, parameters)\n",
    "    \n",
    "        # 计算交叉熵损失\n",
    "        cost = compute_loss(A2, Y)\n",
    "    \n",
    "        # 反向传播\n",
    "        grads = back_propagation(X, Y, parameters, cache)\n",
    "        \n",
    "        # 更新参数\n",
    "        parameters = update_parameters(parameters, grads, learning_rate)\n",
    "        \n",
    "        # print \n",
    "        if (i+1) % 20 == 0:\n",
    "            print('Iteration: %d, cost = %f' % (i+1, cost))\n",
    "    \n",
    "    return parameters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "训练："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration: 20, cost = 0.507616\n",
      "Iteration: 40, cost = 0.439151\n",
      "Iteration: 60, cost = 0.391370\n",
      "Iteration: 80, cost = 0.361023\n",
      "Iteration: 100, cost = 0.342224\n",
      "Iteration: 120, cost = 0.329463\n",
      "Iteration: 140, cost = 0.319611\n",
      "Iteration: 160, cost = 0.311118\n",
      "Iteration: 180, cost = 0.303405\n",
      "Iteration: 200, cost = 0.296525\n",
      "Iteration: 220, cost = 0.290603\n",
      "Iteration: 240, cost = 0.285411\n",
      "Iteration: 260, cost = 0.280535\n",
      "Iteration: 280, cost = 0.275642\n",
      "Iteration: 300, cost = 0.270521\n",
      "Iteration: 320, cost = 0.265057\n",
      "Iteration: 340, cost = 0.259190\n",
      "Iteration: 360, cost = 0.252913\n",
      "Iteration: 380, cost = 0.246268\n",
      "Iteration: 400, cost = 0.239345\n",
      "Iteration: 420, cost = 0.232268\n",
      "Iteration: 440, cost = 0.225162\n",
      "Iteration: 460, cost = 0.218133\n",
      "Iteration: 480, cost = 0.211262\n",
      "Iteration: 500, cost = 0.204605\n"
     ]
    }
   ],
   "source": [
    "parameters = nn_model(X, Y, n_h=3, \n",
    "                      num_iterations=500, \n",
    "                      learning_rate=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 预测模型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义预测函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "def predict(X, parameters):\n",
    "    \"\"\"\n",
    "    函数输入：\n",
    "        - X：神经网络输入\n",
    "        - parameters: 训练完成后的网络参数\n",
    "    函数输出：\n",
    "        - Y_pred: 预测样本标签\n",
    "    \"\"\"\n",
    "    \n",
    "    # 神经网络参数\n",
    "    W1 = parameters['W1']\n",
    "    b1 = parameters['b1']\n",
    "    W2 = parameters['W2']\n",
    "    b2 = parameters['b2']\n",
    "    \n",
    "    # 输入层 —> 隐藏层\n",
    "    Z1 = np.dot(W1, X) + b1\n",
    "    A1 = np.tanh(Z1)\n",
    "    # 隐藏层 —> 输出层\n",
    "    Z2 = np.dot(W2, A1) + b2\n",
    "    A2 = sigmoid(Z2)\n",
    "    \n",
    "    # 预测标签\n",
    "    Y_pred = np.zeros((1, X.shape[1]))    # 初始化 Y_pred\n",
    "    Y_pred[A2 > 0.5] = 1    # Y_hat 大于 0.5 的预测为正类\n",
    "    \n",
    "    return Y_pred"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "预测："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0. 0. 0. 1. 1. 1. 0. 1. 0. 1. 1. 0. 1. 0. 1. 1. 1. 0. 1. 0. 0. 0. 0. 0.\n",
      "  0. 1. 0. 0. 1. 0. 0. 1. 1. 1. 1. 0. 1. 0. 0. 1. 1. 1. 1. 0. 0. 0. 1. 0.\n",
      "  1. 1. 0. 1. 1. 0. 0. 1. 1. 0. 1. 1. 1. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0.\n",
      "  1. 1. 1. 1. 0. 1. 0. 1. 1. 0. 0. 1. 1. 0. 0. 1. 1. 0. 0. 0. 1. 1. 1. 0.\n",
      "  1. 1. 1. 1. 0. 0. 0. 1. 0. 0. 0. 1. 1. 1. 1. 1. 0. 0. 0. 1. 0. 1. 0. 0.\n",
      "  0. 0. 1. 1. 1. 0. 1. 1. 1. 1. 0. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 1. 1. 1.\n",
      "  0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 1. 1. 1. 0. 1. 1. 1. 0. 0. 1.\n",
      "  1. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 1. 0. 1. 0. 1.\n",
      "  1. 1. 0. 1. 1. 0. 1. 0.]]\n"
     ]
    }
   ],
   "source": [
    "Y_pred = predict(X, parameters)\n",
    "print(Y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算预测准确率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.94\n"
     ]
    }
   ],
   "source": [
    "accuracy = np.mean(Y_pred == Y)\n",
    "print(accuracy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "绘制分类直线，可视化分类效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib.colors import ListedColormap\n",
    "    \n",
    "x_min, x_max = X[0, :].min() - 0.5, X[0, :].max() + 0.5\n",
    "y_min, y_max = X[1, :].min() - 0.5, X[1, :].max() + 0.5\n",
    "step = 0.001\n",
    "xx, yy = np.meshgrid(np.arange(x_min, x_max, step), np.arange(y_min, y_max, step))\n",
    "Z = predict(np.c_[xx.ravel(), yy.ravel()].T, parameters)\n",
    "Z = Z.reshape(xx.shape)\n",
    "plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)    # 绘制边界\n",
    "plt.scatter(X[0, Y[0,:]==0], X[1, Y[0,:]==0], c='g', marker='s', label='负类')    # 负类\n",
    "plt.scatter(X[0, Y[0,:]==1], X[1, Y[0,:]==1], c='y', marker='o', label='正类')    # 正类\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
